package com.liujit.web.config;

import com.liujit.common.constants.HeaderConstant;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.RequestParameterBuilder;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author: liujun
 * @date: 2021/6/16 8:11 上午
 * @description: Swagger 配置
 */
@Configuration
@EnableConfigurationProperties({ SwaggerProperties.class })
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class SwaggerConfig {

    /**
     * 默认的排除路径，排除Spring Boot默认的错误处理路径和端点
     */
    private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");

    @Value("${spring.application.name}")
    private String name;

    @Bean
    public Docket api(SwaggerProperties swaggerProperties) {
        // 版本请求头处理
        List<RequestParameter> pars = new ArrayList<>();
        RequestParameterBuilder versionPar = new RequestParameterBuilder().description("灰度路由版本信息")
                .in(ParameterType.HEADER).name(HeaderConstant.VERSION).required(false)
                .query(param -> param.model(model -> model.scalarModel(ScalarType.STRING)));
        pars.add(versionPar.build());
        RequestParameterBuilder descriptionPar = new RequestParameterBuilder().description("菜单id")
                .in(ParameterType.HEADER).name(HeaderConstant.MENU_ID).required(false)
                .query(param -> param.model(model -> model.scalarModel(ScalarType.STRING)));
        pars.add(descriptionPar.build());
        RequestParameterBuilder visitCodePar = new RequestParameterBuilder().description("唯一访问码")
                .in(ParameterType.HEADER).name(HeaderConstant.VISIT_CODE).required(false)
                .query(param -> param.model(model -> model.scalarModel(ScalarType.STRING)));
        pars.add(visitCodePar.build());
        // Host
        ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2)
                // 文档路径
                .host(swaggerProperties.getHost())
                // API 信息
                .apiInfo(apiInfo(swaggerProperties)).groupName(name)
                // 全局参数
                .globalRequestParameters(pars)
                // 选择哪些路径和api会生成document
                .select()
                // 只监控有ApiOperation注解的接口
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class));
        DEFAULT_EXCLUDE_PATH.forEach(p -> builder.paths(PathSelectors.ant(p).negate()));
        return builder.build().securitySchemes(Collections.singletonList(securitySchema(swaggerProperties)))
                .securityContexts(Collections.singletonList(securityContext(swaggerProperties))).pathMapping("/");
    }

    /**
     * API信息
     *
     * @param swaggerProperties
     * @return
     * @author: liujun
     * @date: 2021/6/26 9:05 上午
     */
    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
        return new ApiInfoBuilder().title(swaggerProperties.getTitle()).description(swaggerProperties.getDescription())
                .license(swaggerProperties.getLicense()).licenseUrl(swaggerProperties.getLicenseUrl()).contact(
                        new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(),
                                swaggerProperties.getContact().getEmail())).version(swaggerProperties.getVersion())
                .build();
    }

    /**
     * 配置默认的全局鉴权策略的开关，通过正则表达式进行匹配；默认匹配所有URL
     *
     * @param swaggerProperties
     * @return
     * @author: liujun
     * @date: 2021/6/26 9:12 上午
     */
    private SecurityContext securityContext(SwaggerProperties swaggerProperties) {
        return SecurityContext.builder().securityReferences(defaultAuth(swaggerProperties)).build();
    }

    /**
     * 默认的全局鉴权策略
     *
     * @param swaggerProperties
     * @return
     * @author: liujun
     * @date: 2021/6/26 9:13 上午
     */
    private List<SecurityReference> defaultAuth(SwaggerProperties swaggerProperties) {
        ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        swaggerProperties.getAuthorization().getAuthorizationScopeList().forEach(
                authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(),
                        authorizationScope.getDescription())));
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[authorizationScopeList.size()];
        return Collections.singletonList(
                SecurityReference.builder().reference(swaggerProperties.getAuthorization().getName())
                        .scopes(authorizationScopeList.toArray(authorizationScopes)).build());
    }

    private OAuth securitySchema(SwaggerProperties swaggerProperties) {
        ArrayList<AuthorizationScope> authorizationScopeList = new ArrayList<>();
        swaggerProperties.getAuthorization().getAuthorizationScopeList().forEach(
                authorizationScope -> authorizationScopeList.add(new AuthorizationScope(authorizationScope.getScope(),
                        authorizationScope.getDescription())));
        ArrayList<GrantType> grantTypes = new ArrayList<>();
        swaggerProperties.getAuthorization().getTokenUrlList()
                .forEach(tokenUrl -> grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)));
        return new OAuth(swaggerProperties.getAuthorization().getName(), authorizationScopeList, grantTypes);
    }
}
